package com.hp.bon.sgw.dr;

import static com.hp.bon.sgw.Constants.DIAMETER_AVP_SESSION_ID;

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.bon.security.BonEncrypt;
import com.hp.bon.security.BonSignature;
import com.hp.bon.security.Constants.SecurityResult;
import com.hp.bon.sgw.Constants;
import com.hp.bon.sgw.Constants.MessageType;
import com.hp.bon.sgw.Constants.NodeType;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.core.channel.DCCChannel;
import com.hp.bon.sgw.domain.MessageDetail;
import com.hp.bon.sgw.domain.Node;
import com.hp.bon.sgw.domain.SGWChannel;
import com.hp.bon.sgw.dr.Constants.AsynReturnedCCABySGWFrame;
import com.hp.bon.sgw.dr.Constants.RecvCCAType;
import com.hp.bon.sgw.dr.Constants.RecvCCRType;
import com.hp.bon.sgw.dr.Constants.SendCCRBySGWFrame;
import com.hp.bon.sgw.dr.config.MdrNodeInfo;
import com.hp.bon.sgw.dr.config.MdrmgrConst;
import com.hp.bon.sgw.dr.config.MdrmgrSrvCfg;
import com.hp.bon.sgw.service.ConfigService;
import com.hp.bon.sgw.threads.MessageDetailCacheManager;
import com.hp.bon.sgw.util.dcc.DCCMsgDecorator;
import com.hp.bon.sgw.util.dcc.DccAvpHelper;
import com.hp.ocs.diameter.message.AVP_UTF8String;
import com.hp.ocs.diameter.message.Message;
import com.hp.ocs.diameter.node.NodeSettings;
import com.hp.ocs.diameter.node.NotARequestException;
import com.hp.ocs.diameter.node.NotAnAnswerException;
import com.hp.ocs.diameter.node.Peer;
import com.hp.ocs.diameter.node.StaleConnectionException;

public class DrproxyChannel extends DCCChannel {

	private static final Logger logger = LoggerFactory.getLogger(DrproxyChannel.class);

	public DrproxyChannel(SGWChannel sgwChannel) {
		super(sgwChannel);		
	}

	public int sendRequest(CallParam callParam) {
		SendCCRBySGWFrame ccrType = DrproxyUtils.querySendCCRBySGWFramee(
				callParam.getCapability(), callParam.getToNode().getHostId());// 调用函数获取CCA消息的4种类型
		MdrNodeInfo mdrNodeInfo = MdrmgrSrvCfg.getInstance()
				.queryNodeByRequestToBuzCap(callParam.getCapability());
		switch (ccrType) {
		case BuzMsgNeedDr:
			// DRProxy 本地是主
			if (MdrmgrConst._StrDrStatusMaster.equalsIgnoreCase(mdrNodeInfo
					.getLocalStatusAsThisCap())) {
				// step1 send to master business NE
				this.drproxySendRequest(callParam, true);
				// 判断同步还是跟随模式
				if (mdrNodeInfo._auditMode
						.equalsIgnoreCase(MdrmgrConst._StrAuditModeFollow)) {
					/**
					 * 跟随模式，不对CCR处理，直到等到CCA成功再处理
					 */
					logger.info("AuditMode=Follow wait cca succuess then copy to other drproxy.");
				} else {
					/**
					 * 同步模式，直接copy CCR到另外的DRProxy
					 */
					// step 2 copy and send to other DRProxy
					// 此时只有toPeer, 没有fromPeer。因为，不是收到的消息。
					logger.info("AuditMode=SYNC copy to other drproxy directly.");
					//本地是主，生成CCR，获取对端的DRProxy,发送消息到peer DRProxy
					try {
						//no need to add message to DRProxy Message Queue, send to other DRProxy directly
//						DrproxyUtils.sendCCR2PeerDrproxy((Message) callParam.getSendRequest(), this);
						DrproxyUtils.addDrproxyQueueAndSendCCR2Peer((Message) callParam.getSendRequest(), null, this, DrproxyUtils.otherDrproxyHostId(), true, false);
					} catch (ClassCastException e) {
						logger.warn("Prepared send Dr Request to peer drproxy, but callParam.getSendRequest() is not type of Message. Pls check configuration. Send to peer drproxy failed.");
					}
				}

			} else { // 本地是备
				// step1 send to master business
				// convert to Message, 并且加上auditKey;
				this.drproxySendRequest(callParam, true);
				// step 2 add to DRProxy queue
				if (MdrmgrConst._StrAuditModeFollow.equalsIgnoreCase(mdrNodeInfo._auditMode)) {
					/**
					 * 跟随模式
					 */
					logger.info("AuditMode=Follow, local is slave , wait cca ");
				} else {
					/**
					 * 同步模式,创建CCR',并且发送
					 */
					DrproxyUtils.checkQueAndSend2Slave(this,(Message)callParam.getSendRequest(),mdrNodeInfo.getSlaveNEHostId());
				}
			}
			break;
		case BuzMsgNoDr:
			this.drproxySendRequest(callParam, false);
			break;
		}
		return 0;
	}
	

	public void drproxySendRequest(CallParam callParam, boolean isCreateAuditKey)
	{
		Node recvNode = callParam.getToNode();
		Peer toPeer = (Peer) this.getNodeConnect(recvNode.getHostId());
		Message newMessage;

		// convert to Message;
		// Message newMessage = genBonCallCCR(callParam);
		if (callParam.getRequest() instanceof Message) {

			Message req = (Message) callParam.getRequest();
			newMessage = new Message();

			DccAvpHelper.copyMessageHeader(req.header(), newMessage.header());
			DccAvpHelper.copyMessageAVP(req, newMessage);

		} else if (callParam.getRequest() instanceof XmlMessage) {
			newMessage = super.genBonCallCCR(callParam);
			if (newMessage == null) {
				return ;
			}
		} else {
			LOG.warn("not expected Message type:" + callParam.getRequest().getClass().getSimpleName());
			return ;
		}

		if (newMessage.header().commandCode() == 272) {
			// System.out.println(newMessage.getDictionary().toString());
			DCCMsgDecorator.getInstance().decorateMsg(newMessage, callParam.getFromNode(), recvNode);
			if (isCreateAuditKey)
			{
				String auditKey = DrproxyUtils.createAuditKey(newMessage);
			//add audit key to newMessage
				newMessage.add(new AVP_UTF8String(Constants.DIAMETER_AVP_AUDIT_KEY,Constants.DIAMETER_VENDOR_CT, auditKey));
			}
		}

		callParam.setSendRequest(newMessage);

		// 是否发送至BON
		// 加密
		// 签名
		// ASR,ASA先不考虑了
		if (newMessage.header().commandCode() == 272 &&  recvNode.getNodeType() == NodeType.NODE_SR) {
			String serviceContextId = callParam.getServiceContextId();

			int encrypteCode = BonEncrypt.encrypteMessage(newMessage, serviceContextId, null);
			if (encrypteCode != SecurityResult.SUCCESS) {
				String errorMsg = encrypteCode + ",encrypt message failed:" + callParam.getSessionId();
				LOG.warn(errorMsg);
			}

			int signCode = BonSignature.signMessage(newMessage, serviceContextId, null);
			if (signCode != SecurityResult.SUCCESS) {
				String errorMsg = signCode + ",sign message failed:" + callParam.getSessionId();
				LOG.warn(errorMsg);
			}
		}

		try {
			this.sendRequest(newMessage, toPeer, new Object());

			MessageDetail md = new MessageDetail();
			md.setBusinessCallId(callParam.getBussinessCallId());
			md.setSessionId(callParam.getSessionId());
			md.setServiceContextId(callParam.getServiceContextId());
			md.setMessageType(MessageType.CCR);
			md.setOpType("send");
			md.setHostId(toPeer.hostID());
			md.setMsgTime(new Date(newMessage._rt0));
			md.setDetails(newMessage.getDictionary().toString());
			MessageDetailCacheManager.getInstance().add(md);

		} catch (StaleConnectionException e) {
			e.printStackTrace();
		} catch (NotARequestException e) {
			e.printStackTrace();
		}

		return ;
	}
	public void sendAnswer(Peer fromPeer, CallParam callParam) {		
		Message request = (Message) callParam.getRequest();
		Message answer = (Message) callParam.getResponse(); //此处的应该已经是DCC消息了
		String capName = callParam.getCapability();
		AsynReturnedCCABySGWFrame ccaType = DrproxyUtils.queryAsynReturnedCCABySGWFrame(capName, fromPeer.hostID());//调用函数获取CCA消息的4种类型
		MdrNodeInfo mdrNodeInfo =  MdrmgrSrvCfg.getInstance().queryNodeByRequestFromBuzCap(capName);
		switch (ccaType) {
		case BuzMsgNeedDr:
			super.sendAnswer(fromPeer, callParam);
			/**
			 * 跟随方式
			 */
			// 调用API或者模式，跟随？
			if (mdrNodeInfo._auditMode.equalsIgnoreCase(MdrmgrConst._StrAuditModeFollow))
			{
				
				//由于answer中没有了auditKey，所以，从session类的交易队列中获取原始的CCR的auditKey
				String sessionId=AVP_UTF8String.queryValue(request.find( Constants.DIAMETER_AVP_SESSION_ID ));
				DrCallParam drCallParam=DrproxyMessageQueue.getInstance().removeSessionProgress(sessionId);
				if (drCallParam != null )
				{
					request = drCallParam.getRequest();
					String auditkey = DrproxyUtils.getAuditKey(request);
					
					//add auditKey to answer
					answer.add(new AVP_UTF8String(Constants.DIAMETER_AVP_AUDIT_KEY,Constants.DIAMETER_VENDOR_CT, auditkey));
					
					//包装CCA为CCR发给peer drproxy, 此处不写入交易队列，等收到CCR的时候再决定
//					DrproxyUtils.sendCCA2PeerDrproxy(newMessage,null,this);
					DrproxyUtils.addDrproxyQueueAndSendCCA2PeerDrproxy(answer, fromPeer, this);					
				}
				else
				{
					logger.warn("no session message in drpoxyMessageQueue.");
				}
				
			}
			else
			{
				/**
				 * 同步方式
				 */
				logger.warn("ccaType is BuzMsgNeedDr do not support SYNC mode.");
			}			
			break;
		case BuzMsgNoDr:
			super.sendAnswer(fromPeer, callParam);
			break;
		default:
			logger.warn("ccaType unknow.");
			break;
		}
	}
	

  protected void handleRequest(Message request, Peer peer)
  {
    String capName = DccAvpHelper.getCapability(request);

    RecvCCRType ccrType = DrproxyUtils.queryRecvCCRType(capName, peer.hostID());
    MdrNodeInfo mdrNodeInfo = MdrmgrSrvCfg.getInstance().queryNodeByRequestFromBuzCap(capName);
    if (mdrNodeInfo == null)
      mdrNodeInfo = MdrmgrSrvCfg.getInstance().queryNodeByRequestToBuzCap(capName);
    switch (ccrType)
    {
    case BuzMsgFromSlaveNe:
      super.directReplyCCA(request, peer, 2001, null);

      String drproxyCCRType = DrproxyUtils.getDrproxyCCRType(request);
      if (drproxyCCRType.equals("DRCCR"))
      {
        DrproxyUtils.checkQueAndSend2Slave(this, request, mdrNodeInfo.getSlaveNEHostId());
      }
      else if (mdrNodeInfo._auditMode
        .equalsIgnoreCase("FOLLOW"))
      {
        String auditKey = DrproxyUtils.getAuditKey(request);
        DrCallParam drCallParam = DrproxyMessageQueue.getInstance()
          .removeProgress(auditKey);
        if (drCallParam != null) {
          Message slaveRequest = drCallParam.getRequest();
          Message answer = DrproxyUtils.genAnswerMergeSlaveReqeustAndMasterAnswer(slaveRequest, request);

          diameterSendAnswer(answer, 
            drCallParam.getFromPeer());
        }
        else {
          String slaveHostID = mdrNodeInfo.getSlaveNEHostId();
          DrproxyUtils.addDrproxyQueueAndSendCCR2Peer(request, null, this, slaveHostID, false, true);
          logger.info("received package CCA' from master drproxy, but wait for business slave NE's CCR'. ");
        }

      }
      else
      {
        logger.warn("CCA from drproxy is not exited in SYN mode. pls check dr config.");
      }

      break;
    case BuzMsgNeedDr:
      if ("MASTER".equalsIgnoreCase(mdrNodeInfo.getLocalStatusAsThisCap()))
      {
        if (mdrNodeInfo._auditMode.equalsIgnoreCase("FOLLOW"))
        {
          Peer fromPeer = peer;
          DrproxyUtils.addSessionDrproxyQueue(request, fromPeer, this);

          DrproxyUtils.removeAuditKey(request);

          super.handleRequest(request, peer);
        }
        else
        {
          logger.warn("CCR from master business NE only support follow mode, not support Syn mode.");
        }

      }
      else
      {
        logger.warn("CCR from slave business NE, not support.");
      }
      break;
    case BuzMsgNoDr:
      super.handleRequest(request, peer);
      break;
    case InterMsgBetweenProxy:
      if ("MASTER".equalsIgnoreCase(mdrNodeInfo.getLocalStatusAsThisCap()))
      {
        logger.warn("Master drporxy received CCR from slave business NE, not supported.");
      }
      else if ("FOLLOW".equalsIgnoreCase(mdrNodeInfo._auditMode))
      {
        String auditKey = DrproxyUtils.getAuditKey(request);
        DrCallParam drCallParam = DrproxyMessageQueue.getInstance().removeProgress(auditKey);
        if (drCallParam != null)
        {
          Message drpoxyPackagedCCARequest = drCallParam.getRequest();
          Message answer = DrproxyUtils.genAnswerMergeSlaveReqeustAndMasterAnswer(request, drpoxyPackagedCCARequest);
          Peer toPeer = getNodeConnect(drCallParam.getToPeerHostId());
          diameterSendAnswer(answer, toPeer);
        }
        else
        {
          Peer fromPeer = peer;
          String slaveHostID = mdrNodeInfo.getSlaveNEHostId();
          DrproxyUtils.addDrproxyQueueAndSendCCR2Peer(request, fromPeer, this, slaveHostID, false, true);
        }

      }
      else
      {
        logger.warn("CCR from slave business NE is not supported in SYN Mode.");
      }

      break;
    }
  }

  protected void handleAnswer(Message answer, Peer peer, Message req)
  {
    int resultCode = -1;
    int svrResultCode = -1;

    String capName = DccAvpHelper.getCapability(req);
    RecvCCAType ccaType = DrproxyUtils.queryRecvCCAType(capName, peer.hostID());
    MdrNodeInfo mdrNodeInfo = MdrmgrSrvCfg.getInstance().queryNodeByRequestToBuzCap(capName);
    switch (ccaType)
    {
    case BuzMsgFromSlaveNe:
      logger.info("CCA is from peer drproxy. nothing to do, return!");
      break;
    case BuzMsgNeedDr:
      answer = DrproxyUtils.removeAuditKey(answer);
      super.handleAnswer(answer, peer, req);

      if (mdrNodeInfo._auditMode.equalsIgnoreCase("FOLLOW"))
      {
        resultCode = DccAvpHelper.getCCAMessageResultCode(answer);
        svrResultCode = DccAvpHelper.getCCAMessageServiceResultCode(answer);
        if ((resultCode >= 2000) && (resultCode <= 2999) && (svrResultCode == 0))
        {
          if (mdrNodeInfo._localNodeDrStatus.equals("MASTER"))
          {
            DrproxyUtils.addDrproxyQueueAndSendCCR2Peer(req, null, this, DrproxyUtils.otherDrproxyHostId(), true, false);
          }
          else
          {
            DrproxyUtils.checkQueAndSend2Slave(this, req, mdrNodeInfo.getSlaveNEHostId());
          }
        }
        else
        {
          logger.warn("AuditMode=Follow, master business NE CCA failed, DR message will not copy to peer drproxy.");
        }
      }

      break;
    case BuzMsgNoDr:
      super.handleAnswer(answer, peer, req);
      break;
    case InterMsgBetweenProxy:
      resultCode = DccAvpHelper.getCCAMessageResultCode(answer);
      svrResultCode = DccAvpHelper.getCCAMessageServiceResultCode(answer);
      if ((resultCode >= 2000) && (resultCode <= 2999) && (svrResultCode == 0))
      {
        String auditKey = DrproxyUtils.getAuditKey(answer);

        DrproxyMessageQueue.getInstance().removeProgress(auditKey);
        logger.debug("CCA sucuess message removed from trade message queue, auditKey=" + auditKey);
      }
      else
      {
        logger.warn("CCA failed message could not be removed from trade message queue. wait for querySend().");
      }
      break;
    default:
      logger.warn("ccaType error:" + ccaType);
    }
  }

  public void diameterSendRequest(Message request, Peer peer, Object obj)
  {
    try
    {
      sendRequest(request, peer, obj);
    }
    catch (StaleConnectionException e) {
      e.printStackTrace();
    }
    catch (NotARequestException e) {
      e.printStackTrace();
    } 
  }

  public void diameterSendAnswer(Message msg, Peer toPeer)
  {
    try {
      answer(msg, toPeer);
    }
    catch (NotAnAnswerException e) {
      e.printStackTrace();
    }
  }
}